
/*-------------------------------------------------------------------------
 *
 * nbtvalidate.c
 *	  btree 的操作类验证器。
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * IDENTIFICATION
 *	  src/backend/access/nbtree/nbtvalidate.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "access/amvalidate.h"
#include "access/htup_details.h"
#include "access/nbtree.h"
#include "access/xact.h"
#include "catalog/pg_am.h"
#include "catalog/pg_amop.h"
#include "catalog/pg_amproc.h"
#include "catalog/pg_opclass.h"
#include "catalog/pg_opfamily.h"
#include "catalog/pg_type.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/regproc.h"
#include "utils/syscache.h"


/*
 * btree opclass 的验证器。
 *
 * 这里进行的某些检查涵盖整个 opfamily，因此在检查每个 family 的 opclass 时是冗余的。
 * 但是它们的运行时间不长，因此我们接受重复而不是使 amvalidate API 复杂化。
 */
bool btvalidate(Oid fc_opclassoid)
{
	bool		fc_result = true;
	HeapTuple	fc_classtup;
	Form_pg_opclass fc_classform;
	Oid			fc_opfamilyoid;
	Oid			fc_opcintype;
	char	   *fc_opclassname;
	HeapTuple	fc_familytup;
	Form_pg_opfamily fc_familyform;
	char	   *fc_opfamilyname;
	CatCList   *fc_proclist,
			   *fc_oprlist;
	List	   *fc_grouplist;
	OpFamilyOpFuncGroup *fc_opclassgroup;
	List	   *fc_familytypes;
	int			fc_usefulgroups;
	int			fc_i;
	ListCell   *fc_lc;

	/* 获取操作类信息 */
	fc_classtup = SearchSysCache1(CLAOID, ObjectIdGetDatum(fc_opclassoid));
	if (!HeapTupleIsValid(fc_classtup))
		elog(ERROR, "cache lookup failed for operator class %u", fc_opclassoid);
	fc_classform = (Form_pg_opclass) GETSTRUCT(fc_classtup);

	fc_opfamilyoid = fc_classform->opcfamily;
	fc_opcintype = fc_classform->opcintype;
	fc_opclassname = NameStr(fc_classform->opcname);

	/* 获取操作族信息 */
	fc_familytup = SearchSysCache1(OPFAMILYOID, ObjectIdGetDatum(fc_opfamilyoid));
	if (!HeapTupleIsValid(fc_familytup))
		elog(ERROR, "cache lookup failed for operator family %u", fc_opfamilyoid);
	fc_familyform = (Form_pg_opfamily) GETSTRUCT(fc_familytup);

	fc_opfamilyname = NameStr(fc_familyform->opfname);

	/* 获取操作族的所有操作符和支持函数 */
	fc_oprlist = SearchSysCacheList1(AMOPSTRATEGY, ObjectIdGetDatum(fc_opfamilyoid));
	fc_proclist = SearchSysCacheList1(AMPROCNUM, ObjectIdGetDatum(fc_opfamilyoid));

	/* 检查单个支持函数 */
	for (fc_i = 0; fc_i < fc_proclist->n_members; fc_i++)
	{
		HeapTuple	fc_proctup = &fc_proclist->members[fc_i]->tuple;
		Form_pg_amproc fc_procform = (Form_pg_amproc) GETSTRUCT(fc_proctup);
		bool		fc_ok;

		/* 检查过程编号和函数签名 */
		switch (fc_procform->amprocnum)
		{
			case BTORDER_PROC:
				fc_ok = check_amproc_signature(fc_procform->amproc, INT4OID, true,
											2, 2, fc_procform->amproclefttype,
											fc_procform->amprocrighttype);
				break;
			case BTSORTSUPPORT_PROC:
				fc_ok = check_amproc_signature(fc_procform->amproc, VOIDOID, true,
											1, 1, INTERNALOID);
				break;
			case BTINRANGE_PROC:
				fc_ok = check_amproc_signature(fc_procform->amproc, BOOLOID, true,
											5, 5,
											fc_procform->amproclefttype,
											fc_procform->amproclefttype,
											fc_procform->amprocrighttype,
											BOOLOID, BOOLOID);
				break;
			case BTEQUALIMAGE_PROC:
				fc_ok = check_amproc_signature(fc_procform->amproc, BOOLOID, true,
											1, 1, OIDOID);
				break;
			case BTOPTIONS_PROC:
				fc_ok = check_amoptsproc_signature(fc_procform->amproc);
				break;
			default:
				ereport(INFO,
						(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
						 errmsg("operator family \"%s\" of access method %s contains function %s with invalid support number %d",
								fc_opfamilyname, "btree",
								format_procedure(fc_procform->amproc),
								fc_procform->amprocnum)));
				fc_result = false;
				continue;		/* 不想要额外的消息 */
		}

		if (!fc_ok)
		{
			ereport(INFO,
					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
					 errmsg("operator family \"%s\" of access method %s contains function %s with wrong signature for support number %d",
							fc_opfamilyname, "btree",
							format_procedure(fc_procform->amproc),
							fc_procform->amprocnum)));
			fc_result = false;
		}
	}

	/* 检查单个操作符 */
	for (fc_i = 0; fc_i < fc_oprlist->n_members; fc_i++)
	{
		HeapTuple	fc_oprtup = &fc_oprlist->members[fc_i]->tuple;
		Form_pg_amop fc_oprform = (Form_pg_amop) GETSTRUCT(fc_oprtup);

		/* 检查是否只存在允许的策略编号 */
		if (fc_oprform->amopstrategy < 1 ||
			fc_oprform->amopstrategy > BTMaxStrategyNumber)
		{
			ereport(INFO,
					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
					 errmsg("operator family \"%s\" of access method %s contains operator %s with invalid strategy number %d",
							fc_opfamilyname, "btree",
							format_operator(fc_oprform->amopopr),
							fc_oprform->amopstrategy)));
			fc_result = false;
		}

		/* btree 不支持 ORDER BY 操作符 */
		if (fc_oprform->amoppurpose != AMOP_SEARCH ||
			OidIsValid(fc_oprform->amopsortfamily))
		{
			ereport(INFO,
					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
					 errmsg("operator family \"%s\" of access method %s contains invalid ORDER BY specification for operator %s",
							fc_opfamilyname, "btree",
							format_operator(fc_oprform->amopopr))));
			fc_result = false;
		}

		/* 检查操作符签名 --- 所有 btree 策略相同 */
		if (!check_amop_signature(fc_oprform->amopopr, BOOLOID,
								  fc_oprform->amoplefttype,
								  fc_oprform->amoprighttype))
		{
			ereport(INFO,
					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
					 errmsg("operator family \"%s\" of access method %s contains operator %s with wrong signature",
							fc_opfamilyname, "btree",
							format_operator(fc_oprform->amopopr))));
			fc_result = false;
		}
	}

	/* 现在检查操作符/函数组的不一致性 */
	fc_grouplist = identify_opfamily_groups(fc_oprlist, fc_proclist);
	fc_usefulgroups = 0;
	fc_opclassgroup = NULL;
	fc_familytypes = NIL;
	foreach(fc_lc, fc_grouplist)
	{
		OpFamilyOpFuncGroup *fc_thisgroup = (OpFamilyOpFuncGroup *) lfirst(fc_lc);

		/*
		 * 支持函数 in_range 可能有一个右侧类型
		 * 对 opfamily 来说是无关紧要的 --- 例如，SQL
		 * 要求 datetime_ops opclass 具有带间隔偏移的范围支持。
		 * 因此，如果该组仅似乎包含一个
		 * in_range 函数，请忽略它：它不代表一对
		 * 被支持的类型。
		 */
		if (fc_thisgroup->operatorset == 0 &&
			fc_thisgroup->functionset == (1 << BTINRANGE_PROC))
			continue;

		/* 否则将其计算为相关组 */
		fc_usefulgroups++;

		/* 记住与测试操作类完全匹配的组 */
		if (fc_thisgroup->lefttype == fc_opcintype &&
			fc_thisgroup->righttype == fc_opcintype)
			fc_opclassgroup = fc_thisgroup;

		/*
		 * 确定此 opfamily 中处理的所有不同数据类型。该
		 * 实现的时间复杂度为 O(N^2)，但在 family 中可能不会有足够的
		 * 类型，因此这并不重要。
		 */
		fc_familytypes = list_append_unique_oid(fc_familytypes, fc_thisgroup->lefttype);
		fc_familytypes = list_append_unique_oid(fc_familytypes, fc_thisgroup->righttype);

		/*
		 * 如果发现此数据类型对的操作符或支持函数似乎不完整，则投诉。
		 * sortsupport、in_range 和 equalimage 函数被视为可选。
		 */
		if (fc_thisgroup->operatorset !=
			((1 << BTLessStrategyNumber) |
			 (1 << BTLessEqualStrategyNumber) |
			 (1 << BTEqualStrategyNumber) |
			 (1 << BTGreaterEqualStrategyNumber) |
			 (1 << BTGreaterStrategyNumber)))
		{
			ereport(INFO,
					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
					 errmsg("operator family \"%s\" of access method %s is missing operator(s) for types %s and %s",
							fc_opfamilyname, "btree",
							format_type_be(fc_thisgroup->lefttype),
							format_type_be(fc_thisgroup->righttype))));
			fc_result = false;
		}
		if ((fc_thisgroup->functionset & (1 << BTORDER_PROC)) == 0)
		{
			ereport(INFO,
					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
					 errmsg("operator family \"%s\" of access method %s is missing support function for types %s and %s",
							fc_opfamilyname, "btree",
							format_type_be(fc_thisgroup->lefttype),
							format_type_be(fc_thisgroup->righttype))));
			fc_result = false;
		}
	}

	/* 检查原名操作类是否受支持 */
	/* （如果组存在，我们已经在上面充分检查过） */
	if (!fc_opclassgroup)
	{
		ereport(INFO,
				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
				 errmsg("operator class \"%s\" of access method %s is missing operator(s)",
						fc_opclassname, "btree")));
		fc_result = false;
	}

	/*
	 * 如果 opfamily 没有其支持的数据类型的所有可能组合的条目，则投诉。
	 * 虽然缺少跨类型操作符并不是致命的，但确实限制了规划器从等价类推导
	 * 附加 qual 子句的能力，因此坚持认为所有内置的 btree opfamilies 应该是完整的似乎是合理的。
	 */
	if (fc_usefulgroups != (list_length(fc_familytypes) * list_length(fc_familytypes)))
	{
		ereport(INFO,
				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
				 errmsg("operator family \"%s\" of access method %s is missing cross-type operator(s)",
						fc_opfamilyname, "btree")));
		fc_result = false;
	}

	ReleaseCatCacheList(fc_proclist);
	ReleaseCatCacheList(fc_oprlist);
	ReleaseSysCache(fc_familytup);
	ReleaseSysCache(fc_classtup);

	return fc_result;
}

/*
 * 添加操作符/函数到 btree opfamily 的预检查函数。
 */
void btadjustmembers(Oid fc_opfamilyoid,
				Oid fc_opclassoid,
				List *fc_operators,
				List *fc_functions)
{
	Oid			fc_opcintype;
	ListCell   *fc_lc;

	/*
	 * Btree操作符和比较支持函数如果是跨类型的，始终是opfamily的“松散”成员。如果它们不是跨类型的，我们更倾向于将它们绑定到适当的opclass……但是如果用户没有创建一个，我们就不能这么做，必须退回到使用opfamily依赖关系。（在这种情况下，我们不能强制创建一个opclass，因为留下一个不完整的opclass是不好的。抛出错误是另一个不可取的选择。）
	 *
	 * 这种行为导致了一些转储/重新加载的风险，因为恢复对象的顺序可能会影响我们最终得到的依赖关系。pg_dump现有的行为在大多数情况下将保留依赖选择，但如果一个跨类型操作符被紧密绑定到一个opclass中，则不会。反正这是一个错误，因此默默地“修复”它也不是太糟糕。
	 *
	 * 可选支持函数始终是“松散”家族成员。
	 *
	 * 为了避免重复查找，我们记住最近使用的opclass的输入类型。
	 */
	if (OidIsValid(fc_opclassoid))
	{
		/* 在创建操作类时，需要CCI来查看pg_opclass行 */
		CommandCounterIncrement();
		fc_opcintype = get_opclass_input_type(fc_opclassoid);
	}
	else
		fc_opcintype = InvalidOid;

	/*
	 * 我们几乎一样地处理操作符和支持函数，因此不必重复这个代码块，只需合并列表。
	 */
	foreach(fc_lc, list_concat_copy(fc_operators, fc_functions))
	{
		OpFamilyMember *fc_op = (OpFamilyMember *) lfirst(fc_lc);

		if (fc_op->is_func && fc_op->number != BTORDER_PROC)
		{
			/* 可选支持过程，因此始终是软性家族依赖 */
			fc_op->ref_is_hard = false;
			fc_op->ref_is_family = true;
			fc_op->refobjid = fc_opfamilyoid;
		}
		else if (fc_op->lefttype != fc_op->righttype)
		{
			/* 跨类型，因此始终是软性家族依赖 */
			fc_op->ref_is_hard = false;
			fc_op->ref_is_family = true;
			fc_op->refobjid = fc_opfamilyoid;
		}
		else
		{
			/* 不是跨类型；是否有合适的操作类？ */
			if (fc_op->lefttype != fc_opcintype)
			{
				/* 避免重复这个昂贵的查找，即使它失败 */
				fc_opcintype = fc_op->lefttype;
				fc_opclassoid = opclass_for_family_datatype(BTREE_AM_OID,
														 fc_opfamilyoid,
														 fc_opcintype);
			}
			if (OidIsValid(fc_opclassoid))
			{
				/* 对操作类的硬性依赖 */
				fc_op->ref_is_hard = true;
				fc_op->ref_is_family = false;
				fc_op->refobjid = fc_opclassoid;
			}
			else
			{
				/* 我们被卡住了，因此对操作族建立软性依赖 */
				fc_op->ref_is_hard = false;
				fc_op->ref_is_family = true;
				fc_op->refobjid = fc_opfamilyoid;
			}
		}
	}
}
